home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 4
/
Aminet 4 - November 1994.iso
/
aminet
/
comm
/
net
/
dnet2_10_13.lha
/
DNet
/
Amiga
/
Sourcen.lha
/
dnet
/
control.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-01-14
|
13KB
|
574 lines
/*
* CONTROL.C
*
* DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
*
* Main packet control. Medium level. Interprets formatted received
* packets and sends formatted tx packets. Uses low level packet
* construction and reception routines in PACKET.C
*/
#include "dnet.h"
#include <stdio.h>
void do_rupdate();
void do_reccmd();
void replywindow();
void StartWriteTimeout();
/*
* RTO: read timeout. Timeout occured while waiting for the rest of
* a packet. Reset state and restart read.
*
* called from iosink loop, ok if signal mask cleared
*/
void
do_rto(ior)
IOT *ior;
{
NetStartRead(RecvPacket(NULL, 0));
}
/*
* WTO: Write timeout (unresolved output windows exist). Resend a CHECK
* command for all unresolved (READY) output windows
*/
void
do_wto(ior)
IOT *ior;
{
short i;
PKT *pkt;
short to = 0;
if (Restart) {
WriteRestart();
return;
}
for (i = 0; i < WPUsed; ++i) {
pkt = WPak[i];
if (pkt->state == READY) {
WriteChk((WPStart + i) & 7);
++to;
}
}
if (to)
StartWriteTimeout(8);
}
void
do_rnet(ptr, len)
ubyte *ptr;
long len;
{
long expect;
long n;
if (len < 0)
ptr = NULL;
expect = RecvPacket(ptr, len);
n = NetReady();
if (n > expect)
expect = n;
NetStartRead(expect);
}
/*
* StartWriteTimeout()
*
* Begins a timeout for written packets. This timeout normally never
* occurs even with data errors (the receiver detects missing windows
* and sends the appropriate NAK).
*/
void
StartWriteTimeout(secs)
{
if (Wto_act) {
AbortIO((IOR *)&Wto);
WaitIO((IOR *)&Wto);
Wto_act = 0;
}
if (secs) {
Wto.tr_time.tv_secs = secs;
Wto.tr_time.tv_micro= 0;
SendIO((IOR *)&Wto);
Wto_act = 1;
}
}
/*
* DO_WUPDATE()
*
* (1) Remove EMPTY windows at head of transmit queue.
* (2) Fill up transmit queue with pending requests, if any.
* (3) Start timeout for write if did send packet.
*
* CMD 0x20-0x3F 0-31 bytes data
* 0x40-0x7F 0-N bytes data (w/ extend byte 0x20-0x7F)
* 0x80-0xFF Command.
*
* Note that normal data encoding overhead uses 0x20-0x7F,
* thereby allowing 7 bit data to be transmitted with normal
* packets instead of expanded (8->6 bits) packets.
*/
void
do_wupdate()
{
static short XPri;
static ubyte DBuf[MAXPACKET];
int maxpktsize;
IOSTD *ior;
PKT *pkt;
long len;
char wrotePkt = 0;
while (WPUsed && WPak[0]->state == EMPTY) {
pkt = WPak[0];
WPak[0] = WPak[1];
WPak[1] = WPak[2];
WPak[2] = WPak[3];
WPak[3] = pkt;
--WPUsed;
++WPStart;
}
if (Restart)
return;
/*
* Fill new packets and send.
*/
while (WPUsed != 4 && (ior = (IOSTD *)RemHead(&TxList))) {
long offset = 0;
if (DDebug)
printf("Extract Request: %08lx %ld bytes\n", ior, ior->io_Length);
{
short npri;
if (ior->io_Command == SCMD_DATA) {
ubyte chan = (ulong)ior->io_Unit;
if (Chan[chan].state == CHAN_CLOSE) { /* channel closed */
ior->io_Error = 1;
ReplyMsg((MSG *)ior);
continue;
}
npri = ior->io_Message.mn_Node.ln_Pri << 2;
} else {
npri = XPri;
}
if (npri >= XPri)
XPri = npri;
else {
if (XPri - npri > 100)
XPri -= 10;
else if (XPri - npri > 50)
XPri -= 5;
else
--XPri;
}
maxpktsize = MAXPKT - (XPri - npri);
if (maxpktsize < MINPKT)
maxpktsize = MINPKT;
}
pkt = WPak[WPUsed];
pkt->state = READY;
for (;;) {
if (offset > (maxpktsize-8)) /* not enough room */
break;
if (ior->io_Command == SCMD_DATA && (ulong)ior->io_Unit != WChan) {
/* CSWITCH */
WChan = (ulong)ior->io_Unit;
DBuf[offset+0] = 0x80|SCMD_SWITCH|(2<<3);
DBuf[offset+1] = WChan >> 8;
DBuf[offset+2] = WChan;
offset += 3;
}
len = ior->io_Length - ior->io_Actual;
if (ior->io_Command == SCMD_DATA) { /* DATA OUT */
if (offset + len > (maxpktsize-4))
len = (maxpktsize-4) - offset;
if (len < 0x20) {
DBuf[offset] = len + 0x20;
++offset;
} else {
DBuf[offset+0] = 0x40 + len / 96;
DBuf[offset+1] = 0x20 + len % 96;
offset += 2;
}
BytesOut += len;
} else { /* COMMAND OUT */
DBuf[offset] = 0x80|ior->io_Command|(len<<3);
++offset;
}
BMov((char *)ior->io_Data + ior->io_Actual, DBuf + offset, len);
offset += len;
ior->io_Actual += len;
if (ior->io_Actual == ior->io_Length) {
ReplyMsg((MSG *)ior);
ior = (IOSTD *)RemHead(&TxList); /* Next packet */
if (ior == NULL)
break;
}
}
BuildDataPacket(pkt, (WPStart + WPUsed) & 7, DBuf, offset);
WritePacket(pkt);
wrotePkt = 1;
if (ior) {
++ior->io_Message.mn_Node.ln_Pri;
Enqueue(&TxList, (NODE *)ior);
--ior->io_Message.mn_Node.ln_Pri;
}
++WPUsed;
++PacketsOut;
ResetIdle();
break; /* One at a time, else would take too long */
}
if (wrotePkt)
StartWriteTimeout(8);
}
void
dumpcheck(ptr)
ubyte *ptr;
{
short i;
for (i = 0; i < 8; ++i) {
if (ptr[i])
replywindow(i);
ptr[i] = 0;
}
}
void
do_cmd(ctl, buf, bytes)
uword ctl; /* usually just 8 bits though */
ubyte *buf;
{
ubyte window = ctl & PKF_SEQUENCE;
ubyte rwindow;
static ubyte Chk, Chkwin[8];
if (ctl == 0xFFFF) {
if (Chk) {
dumpcheck(Chkwin);
Chk = 0;
}
return;
}
if (DDebug)
printf("RECV-PACKET %02x %4ld bytes\n", ctl, bytes);
if ((ctl & PKF_MASK) == PKCMD_CHECK) {
Chkwin[window] = 1;
Chk = 1;
return;
}
if (Chk) {
dumpcheck(Chkwin);
Chk = 0;
}
switch(ctl & PKF_MASK) {
case PKCMD_WRITE:
case PKCMD_WRITE6:
case PKCMD_WRITE7:
rwindow = (window - RPStart) & 7;
if (rwindow < 4) {
BMov(buf, RPak[rwindow]->data, bytes);
RPak[rwindow]->buflen = bytes; /* dummy */
RPak[rwindow]->state = READY;
do_rupdate();
/*
* Check for missing receive packet. rwindow 1..3 and
* (rwindow - 1) != READY
*/
if (rwindow && RPak[rwindow-1]->state != READY)
WriteNak((window - 1) & 7);
}
replywindow(window);
break;
case PKCMD_ACK:
rwindow = (window - WPStart) & 7;
if (rwindow < WPUsed) /* mark as sent */
WPak[rwindow]->state = EMPTY;
break;
case PKCMD_NAK: /* resend */
rwindow = (window - WPStart) & 7;
if (rwindow < WPUsed) { /* resend */
++PacketsResent;
WritePacket(WPak[rwindow]);
StartWriteTimeout(8);
} else {
printf("Soft Error: Illegal NAK: %ld %ld %ld %ld\n",
window, WPStart, rwindow, WPUsed
);
}
break;
case PKCMD_RESTART:
case PKCMD_ACKRSTART:
if ((ctl & PKF_MASK) == PKCMD_ACKRSTART)
Restart = 0;
do_netreset();
/* RxPtr? */
if ((ctl & PKF_MASK) == PKCMD_RESTART) {
static ubyte buf[32];
strcpy(buf, " ");
/*
* note, restart packet may contain only ascii 0x20-0x7F
* 00 is definitely out.
*/
WritePacket(BuildRestartAckPacket(buf, strlen(buf)));
StartWriteTimeout(5);
}
break;
}
do_rupdate();
}
/*
* Multiplex data onto the low level stream
*
* 0x20-0x3F 0-31 bytes of data for current channel
* 0x80-0xBF control-command w/0-7 bytes of data
* 0x40-0x7F this + extend byte (0x20-0x7F) for long data pkts
*/
void
do_rupdate()
{
while (RPak[0]->state == READY) {
PKT *pkt = RPak[0];
ubyte *ptr = pkt->data;
uword len;
uword iolen = pkt->buflen;
ubyte cmd;
while (iolen) {
cmd = SCMD_DATA;
len = ptr[0];
++ptr;
--iolen;
if (len >= 128) {
cmd = len & 7;
len = (len >> 3) & 7;
} else {
if (len < 0x40) {
len -= 0x20;
if (len < 0) {
printf("HLP len error1 %d\n", len);
len = 0;
}
} else {
if (*ptr < 0x20)
printf("HLP len error2 %02x %02x\n", len, *ptr);
len = (len - 0x40) * 96 + (*ptr - 0x20);
++ptr;
--iolen;
}
}
iolen -= len;
if (DDebug)
printf("RECEIVE CMD %2ld ", cmd);
do_reccmd(cmd, ptr, len);
ptr += len;
}
RPak[0] = RPak[1];
RPak[1] = RPak[2];
RPak[2] = RPak[3];
RPak[3] = pkt;
pkt->state = EMPTY;
++RPStart;
}
}
void
do_reccmd(cmd, ptr, len)
int cmd;
ubyte *ptr;
int len;
{
switch(cmd) {
case SCMD_DATA: /* data for channel */
if (RChan < MAXCHAN && (Chan[RChan].flags & CHANF_ROK)) {
IOSTD *ior = AllocMem(sizeof(IOSTD), MEMF_PUBLIC);
ior->io_Unit = (struct Unit *)RChan;
ior->io_Data = AllocMem(len, MEMF_PUBLIC);
ior->io_Length = len;
ior->io_Actual = 0;
BMov(ptr, ior->io_Data, len);
ior->io_Message.mn_Node.ln_Name = (char *)PKT_REQ;
ior->io_Command = DNCMD_WRITE;
ior->io_Message.mn_ReplyPort = IOSink;
PutMsg(Chan[RChan].port, (MSG *)ior);
BytesIn += len;
ResetIdle(); /* not idle, have received data */
}
break;
case SCMD_SWITCH:
RChan = (ptr[0]<<8)|ptr[1];
break;
case SCMD_OPEN:
{
COPEN *cop = (COPEN *)ptr;
PORT *port;
CACKCMD ack;
char buf[32];
uword chan = (cop->chanh << 8) | cop->chanl;
uword portnum = (cop->porth << 8) | cop->portl;
ack.chanh = cop->chanh;
ack.chanl = cop->chanl;
ack.error = 0;
if (chan >= MAXCHAN || Chan[chan].state) {
ack.error = 33; /* magic */
WriteStream(SCMD_ACKCMD, &ack, sizeof(CACKCMD), (uword)-1);
break;
}
sprintf(buf, "DNET.PORT.%ld", portnum);
if ((port = (PORT *)FindPort(buf)) == NULL) {
RunServer(portnum);
if ((port = (PORT *)FindPort(buf)) == NULL) {
ack.error = 2;
WriteStream(SCMD_ACKCMD, &ack, sizeof(CACKCMD), (uword)-1);
break;
}
}
/* ln_Name type of 0 causes reply to go to DNetPort */
WritePort(port, DNCMD_SOPEN, NULL, 0, 0, chan);
Chan[chan].state = CHAN_ROPEN;
Chan[chan].pri = cop->pri;
}
break;
case SCMD_CLOSE: /* receive close */
{
CCLOSE *clo = (CCLOSE *)ptr;
uword chan = (clo->chanh<<8)|clo->chanl;
if (DDebug)
printf("Remote close, chan %d state %d\n", chan, Chan[chan].state);
if (chan >= MAXCHAN || Chan[chan].state == CHAN_FREE) {
break;
}
Chan[chan].state = CHAN_CLOSE;
Chan[chan].flags |= CHANF_RCLOSE;
Chan[chan].flags &= ~(CHANF_ROK|CHANF_WOK);
if (Chan[chan].flags & CHANF_LCLOSE) {
if (DDebug)
printf("Local already closed, reply %08lx\n", Chan[chan].ior);
Chan[chan].state = CHAN_FREE;
ReplyMsg((MSG *)Chan[chan].ior);
Chan[chan].ior = NULL;
} else { /* send EOF */
if (DDebug)
printf("Local not already closed\n");
WritePort(Chan[chan].port, DNCMD_CLOSE, NULL, 0, PKT_REQ, chan);
}
}
break;
case SCMD_ACKCMD: /* acknowledge of my open */
{
CACKCMD *cack = (CACKCMD *)ptr;
uword chan = (cack->chanh<<8)|cack->chanl;
if (chan >= MAXCHAN || Chan[chan].state != CHAN_LOPEN) {
break;
}
/*
* Channel in use (collision), try again
*/
if (cack->error == 33) {
uword newchan = alloc_channel();
COPEN co;
if (newchan < MAXCHAN) {
Chan[newchan] = Chan[chan];
Chan[chan].state = CHAN_FREE;
Chan[chan].ior = NULL;
co.chanh = newchan >> 8;
co.chanl = newchan;
co.porth = (ulong)Chan[newchan].ior->io_Unit >> 8;
co.portl = (ulong)Chan[newchan].ior->io_Unit;
co.error = 0;
co.pri = Chan[chan].pri;
WriteStream(SCMD_OPEN, &co, sizeof(COPEN), chan);
break;
}
}
if (cack->error) {
Chan[chan].state = CHAN_FREE;
Chan[chan].ior->io_Error = cack->error;
ReplyMsg((MSG *)Chan[chan].ior);
Chan[chan].ior = NULL;
} else {
Chan[chan].state = CHAN_OPEN;
Chan[chan].ior->io_Error = 0;
Chan[chan].ior->io_Unit = (struct Unit *)chan;
Chan[chan].flags = CHANF_ROK|CHANF_WOK;
ReplyMsg((MSG *)Chan[chan].ior);
Chan[chan].ior = NULL;
}
}
break;
case SCMD_EOFCMD: /* EOF on channel */
{
CEOFCMD *eof = (CEOFCMD *)ptr;
uword chan = (eof->chanh<<8)|eof->chanl;
if (chan < MAXCHAN && Chan[chan].state == CHAN_OPEN) {
Chan[chan].flags &= ~eof->flags;
if (eof->flags & CHANF_ROK)
WritePort(Chan[chan].port, DNCMD_EOF, NULL, 0, PKT_REQ, chan);
} else {
printf("SCMD_EOFCMD: Error chan %ld state %ld\n",
chan, Chan[chan].state
);
}
}
break;
case SCMD_IOCTL:
{
CIOCTL *cio = (CIOCTL *)ptr;
uword chan = (cio->chanh<<8)|cio->chanl;
if (Chan[chan].state == CHAN_OPEN)
WritePort(Chan[chan].port, DNCMD_IOCTL, cio, sizeof(*cio), PKT_REQ, chan);
}
break;
default:
if (DDebug)
printf("BAD SCMD, %ld\n", cmd);
break;
}
}
void
replywindow(window)
int window;
{
ubyte rwindow = (window - RPStart) & 7;
if (rwindow >= 4 || RPak[rwindow]->state == READY) { /* data ready */
WriteAck(window);
++PacketsIn;
} else {
WriteNak(window);
++PacketsNakd;
}
}